﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Qreed.Windows.Forms.Hook
{
    /// <summary>
    /// The event arguments used by the <see cref="CbtLocalHook"/> events.
    /// </summary>
    public class CbtHookEventArgs : EventArgs
    {
        internal CbtHookEventArgs(IntPtr handle, string title, string className, bool isDialogWindow)
        {
            Handle = handle;
            Title = title;
            ClassName = className;
            IsDialogWindow = isDialogWindow;
        }

        /// <summary>
        /// The window handle of the window we've hooked.
        /// </summary>
        public IntPtr Handle;
        /// <summary>
        /// The title text of the window we've hooked.
        /// </summary>
        public string Title;
        /// <summary>
        /// The class name of the window we've hooked.
        /// </summary>
        public string ClassName;
        /// <summary>
        /// Indicates if the hooked window is a dialog window.
        /// </summary>
        public bool IsDialogWindow;
    }

    /// <summary>
    /// Allows hooking of general window messages.
    /// </summary>
    public class CbtLocalHook : LocalHook
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CbtLocalHook"/> class.
        /// </summary>
        public CbtLocalHook() : this(null) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="CbtLocalHook"/> class.
        /// </summary>
        /// <param name="proc">The proc.</param>
        public CbtLocalHook(HookProc proc)
            : base(User32.HookType.WH_CBT, proc)
        {
            base.Invoked += new EventHandler<HookEventArgs>(CbtLocalHook_Invoked);
        }

        private IntPtr _windowHandle = IntPtr.Zero;
        private string _windowTitle = String.Empty;
        private string _windowClass = String.Empty;
        private bool _isDialog;

        private void CbtLocalHook_Invoked(object sender, HookEventArgs e)
        {
            User32.HookAction action = (User32.HookAction)e.Code;
            UpdateWindowDate(e.wParam);

            switch (action)
            {
                case User32.HookAction.HCBT_CREATEWND:
                    OnWindowCreated();
                    break;

                case User32.HookAction.HCBT_ACTIVATE:
                    OnWindowActivated();
                    break;

                case User32.HookAction.HCBT_DESTROYWND:
                    OnWindowDestroyed();
                    break;
            }
        }

        /// <summary>
        /// Occurs when the hooked window is destroyed.
        /// </summary>
        public event EventHandler<CbtHookEventArgs> WindowDestroyed;

        /// <summary>
        /// Called when the hooked window is destroyed.
        /// </summary>
        protected virtual void OnWindowDestroyed()
        {
            if (WindowDestroyed != null)
                WindowDestroyed(this, new CbtHookEventArgs(_windowHandle, _windowTitle, _windowClass, _isDialog));
        }

        /// <summary>
        /// Occurs when the hooked window is activated.
        /// </summary>
        public event EventHandler<CbtHookEventArgs> WindowActivated;

        /// <summary>
        /// Called when the hooked window is activated.
        /// </summary>
        protected virtual void OnWindowActivated()
        {
            if(WindowActivated != null)
                WindowActivated(this, new CbtHookEventArgs(_windowHandle, _windowTitle, _windowClass, _isDialog));
        }

        /// <summary>
        /// Occurs when the hooked window is created.
        /// </summary>
        public event EventHandler<CbtHookEventArgs> WindowCreated;

        /// <summary>
        /// Called when the hooked window is created.
        /// </summary>
        protected virtual void OnWindowCreated()
        {
            if(WindowCreated != null)
                WindowCreated(this, new CbtHookEventArgs(_windowHandle, _windowTitle, _windowClass, _isDialog));
        }

        private void UpdateWindowDate(IntPtr windowHandle)
        {
            _windowHandle = windowHandle;

            StringBuilder sb = new StringBuilder(256);
            User32.GetClassName(_windowHandle, sb, 40);
            _windowClass = sb.ToString();

            User32.GetWindowText(_windowHandle, sb, 256);
            _windowTitle = sb.ToString();

            _isDialog = (_windowClass == "#32770");
        }
    }
}
